type Listener<T = any> = (data: T) => void;

export class EventEmitter<EventType extends string> {
    private events: Map<EventType, Listener[]> = new Map();

    on(event: EventType, listener: Listener) {
        const listeners = this.events.get(event) || [];
        listeners.push(listener);
        this.events.set(event, listeners);
        return this;
    }

    once(event: EventType, listener: Listener) {
        const onceWrapper = (data: any) => {
            listener(data);
            this.off(event, onceWrapper);
        };
        return this.on(event, onceWrapper);
    }

    off(event: EventType, listener?: Listener) {
        if (!listener) {
            this.events.delete(event);
            return;
        }

        const listeners = this.events.get(event);
        if (listeners) {
            this.events.set(
                event,
                listeners.filter((l) => l !== listener)
            );
        }
    }

    emit(event: EventType, data?: any) {
        const listeners = this.events.get(event);
        listeners?.forEach((listener) => listener(data));
    }

    removeAllListeners() {
        this.events.clear();
    }
}
